﻿using System;
using System.ComponentModel;
using nRoute.Utilities;
using System.Reflection;
using System.Globalization;
using System.Text.RegularExpressions;
#if (WINRT)
using Windows.UI.Xaml.Media;
using Windows.UI;
#endif

namespace nRoute.Components.TypeConverters
{
    public static class ConverterHelper
    {

#if (!WINRT)

        public static object ConvertToType(object value, Type type)
        {
            if (value != null)
            {
                if (type.IsAssignableFrom(value.GetType()))
                {
                    return value;
                }
                var _typeConverter = GetTypeConverter(type);
                if ((_typeConverter != null) && _typeConverter.CanConvertFrom(value.GetType()))
                {
                    value = _typeConverter.ConvertFrom(value);
                    return value;
                }
            }
            return null;
        }

        public static TypeConverter GetTypeConverter(Type type)
        {
            var _attribute = (TypeConverterAttribute)Attribute.GetCustomAttribute(type, typeof(TypeConverterAttribute), false);
            if (_attribute != null)
            {
                try
                {
                    Type _converterType = Type.GetType(_attribute.ConverterTypeName, false);
                    if (_converterType != null)
                    {
                        return (Activator.CreateInstance(_converterType) as TypeConverter);
                    }
                }
                catch
                {
                    // note we absorb this as this is xaml related
                }
            }

#if SILVERLIGHT
            return new ConvertFromStringConverter(type);
#else
            return TypeDescriptor.GetConverter(type);
#endif            
        }
#else

        // note no type converters in WinRT, http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/506727b4-deae-4db9-b8de-1c9e4b746a6e
        public static object ConvertToType(object value, Type type)
        {
            if (value != null)
            {
                if (type.IsAssignableFrom(value.GetType()))
                {
                    return value;
                }

                // for enums
                if (type.GetTypeInfo().IsEnum && (value is string))
                {
                    return Enum.Parse(type, Convert.ToString(value), true);
                }

                // for colors
                if (type.IsAssignableFrom(typeof(Brush)))
                {
                    var _color = ResolveColor(Convert.ToString(value));
                    if (_color.HasValue) return new SolidColorBrush(_color.Value);
                }

                // all else
                return Convert.ChangeType(value, type);
            }
            return null;
        }

#region Helpers

        // from http://yinyangme.com/blog/post/ColorFromString-for-Silverlight-or-NET.aspx
        private static Color? ResolveColor(string value)
        {
            Guard.ArgumentNotNullOrWhiteSpace(value, "value");
 
            // Named Colors
            if (value[0] != '#')
            {
                return ResolveColorByName(value);
            }
            else
            {
                // #ARGB and #RGB Hex Colors
                value = value.Remove(0, 1);
            }
 
            int length = value.Length;
            if ((length == 6 || length == 8) && IsHexColor(value))
            {
                if (length == 8)
                    return Color.FromArgb(
                    byte.Parse(value.Substring(0, 2), NumberStyles.HexNumber),
                    byte.Parse(value.Substring(2, 2), NumberStyles.HexNumber),
                    byte.Parse(value.Substring(4, 2), NumberStyles.HexNumber),
                    byte.Parse(value.Substring(6, 2), NumberStyles.HexNumber));
 
                if (length == 6)
                    return Color.FromArgb(0xff,
                    byte.Parse(value.Substring(0, 2), NumberStyles.HexNumber),
                    byte.Parse(value.Substring(2, 2), NumberStyles.HexNumber),
                    byte.Parse(value.Substring(4, 2), NumberStyles.HexNumber));
            }
 
            // A,R,G,B and R,G,B Colors
            string[] argb = value.Split(
            new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (argb != null)
            {
                if (argb.Length == 4)
                    return Color.FromArgb(
                    byte.Parse(argb[0]), byte.Parse(argb[1]), byte.Parse(argb[2]),
                    byte.Parse(argb[3]));
 
                if (argb.Length == 3)
                    return Color.FromArgb(0xff,
                    byte.Parse(argb[0]), byte.Parse(argb[1]), byte.Parse(argb[2]));
            }
 
            return null;
        }
 
        private static bool IsHexColor(string value)
        {
            if (value == null)
                return false;

            // http://stackoverflow.com/questions/223832/check-a-string-to-see-if-all-characters-are-hexadecimal-values
            // For C-style hex notation (0xFF) you can use @"\A\b(0[xX])?[0-9a-fA-F]+\b\Z"
            return Regex.IsMatch(value, @"\A\b[0-9a-fA-F]+\b\Z");
        }

        private static Color? ResolveColorByName(string value)
        {
            var _colorPropInfo = typeof(Colors).GetTypeInfo().GetDeclaredProperty(value);
            if (_colorPropInfo != null)
            {
                return (Color)_colorPropInfo.GetValue(default(Colors));
            }
            return null;
        }

#endregion

#endif

    }
}